/**
 * Semplice programma di test della libreria LC di Paolo Giangrandi.
 * Il programma crea due processi (più quello iniziale) corrispondenti a tre
 * stage di una pipeline, che incrementa di uno (ad ogni stage) i numeri generati
 * dal primo. Il valore -1 è il carattere di fine stream
 *
 * E' ASSENTE LA GESTIONE DI EVENTUALI ERRORI
 * PER COMPILARE: gcc simple_pipeline.c include/*.c -lpthread
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "../Include/channel.h"


//int core_mapping[16] = { 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 };
int core_mapping[]={0};
/*
   Emitter, gli passiamo come parametro il canale in uscita e il numero di elementi da generare
*/
void first_stage(key_t ch_out, int numelem)
{
   // Attach to the channel
	Channel out;
    int vect[]={1,2,3,4,5,6,7,8,9,10,11,12,13};
	Communicator kp=NULL;
   int i;

  /* #ifdef DEBUG
      printf("First stage. Mi collego al canale con chiave %d\n",ch_out);
   #endif*/

   out=  LCattach(ch_out);
   /*#ifdef DEBUG
      printf("First stage attached\n");
   #endif*/
   printf("-First stage creo communicator...\n");
   kp=initCommunicator(core_mapping);
   printf("-First stage communicator creato!\n");
   Delegation d;
   for(i=0;i<numelem;i++)
   {
       d=LCbsend(kp,out,(void *)(&vect[i%12]));
      #ifdef DEBUG
         printf("-First stage inviato %d\n",i);
      #endif

      printf("Aspetto la fine della send...\n");
      //LCwait(d);
      printf("Send terminata!\n");

   }
   //fine stream
   i=-1;
   LCsend(kp,out,(void *)(&i));

   //terminate the communicator
   finalizeCommunicator(kp);

   //rilascia il canale
   LCdetatch(out);
}

/*
   Secondo stage, ha bisogno dei riferimenti ai canali in ingresso e in uscita
*/
void second_stage(key_t ch_in, key_t ch_out)
{
   // Attach to the channel
   Channel in,out;
   Communicator kp=NULL;
  /* #ifdef DEBUG
      printf("Second stage.Mi collego al canale con chiave %d e in uscita %d\n",ch_in,ch_out);
   #endif*/
   in = LCattach(ch_in);
   /*#ifdef DEBUG
      printf("Second stage attached in input\n");
   #endif*/
	out= LCattach(ch_out);
	/*#ifdef DEBUG
      printf("Second stage attached in output\n");
   #endif*/
   int *rcv;
   kp=initCommunicator(core_mapping);
   do
   {
      //riceviamo
      rcv=(int *)LCreceive(in);
      #ifdef DEBUG
         printf("-Second stage: received %d\n",*rcv);
      #endif
      if(*rcv!=-1)
      {
         *rcv=*rcv+1;
         #ifdef DEBUG
            printf("-Second stage: sending %d\n",*rcv);
         #endif
         LCsend(kp,out,(void *)rcv);
      }
      else
         LCsend(kp,out,(void *)rcv);
   }while(*rcv!=-1);

    //terminate the communicator
   finalizeCommunicator(kp);
   //distruggiamo il canale in ingresso e detatchiamo quello in uscita
   LCdestroy(in);
   LCdetatch(out);

  // sleep(2);
   exit(0);

}

/*
   Terzo stage, ha bisogno del riferimento al canale in ingresso
*/
void third_stage(key_t ch_in)
{
   // Attach to the channel
   Channel in ;
   /*#ifdef DEBUG
      printf("Third stage. Mi collego al canale %d\n",ch_in);
   #endif*/
   in = LCattach(ch_in);
   /*#ifdef DEBUG
      printf("third stage attached\n");
   #endif*/
   int *rcv;
   do
   {
      //riceviamo
      rcv=(int *)LCreceive(in);
      if(*rcv!=-1)
      {
          printf("*** Output = %d ***\n",*rcv);
      }
   }while(*rcv!=-1);

   LCdestroy(in);
   //sleep(1);
   exit(0);

}

int main(int argc, char *argv[])
{
   pid_t pid;
   pid_t pids[2];
   Channel tmpchan;
   int i;
   /* Creazione canali: poichè è possibile tenere allocato in memoria principale un segmento
   * condiviso anche se non è presente nella memoria virtuale di nessun processo. Percio' i canali vengono
   * creati qui e immediatamente detatched in maniera tale che non vengano poi condivisi tra tutti i processi
   * a seguito della fork
   */

   // Alloco lo spazio per le chiavi, che saranno due come i canali da creare. Una chiave si riferisce a un descrittore
   // di canale (sarà usata nella shmget)
   key_t *keys = malloc(sizeof(key_t) *2 );

   //assegnamo i valori alle chiavi; per farlo in maniera univoca usiamo una ganza funzione: la ftok
   for(i=0;i<2;i++)
   {
      keys[i]=ftok(argv[0],i);
      #ifdef DEBUG
         printf("Creazione chiavi: creata chiave %d con valore %d\n",i,keys[i]);
      #endif
   }
   //creiamo i canali e immediatamente li detatchiamo
   for(i=0;i<2;i++)
   {
      #ifdef DEBUG
         printf("main: creo canale con chiave %d\n", keys[i]);
      #endif

      tmpchan=LCcreate_as(keys[i],sizeof(int),10,10); //j=10 per evitare rogne
      LCdetatch(tmpchan);
   }

   //a questo punto creiamo i vari processi, andandoli a specificare e passandogli i parametri richiesti
   pid=fork();
   if(pid==0) //child
   {
      pid=fork();
      if(pid==0) //child
      {
         third_stage(keys[1]);

      }
      else
      {
         pids[1]=pid;
         second_stage(keys[0],keys[1]);
      }
   }
   else
   {
      pids[0]=pid;
      first_stage(keys[0],10);
   }

   // Wait for the process to quit
	for (i = 0; i < 2; i++) {
		int childExitStatus;
		waitpid( pids[i] , &childExitStatus, 0);
	}
   return 0;
}
